/**
 * Copyright (C) 2009 - 2012 SC 4ViewSoft SRL
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.achartengine.chart;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.SimpleSeriesRenderer;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYMultipleSeriesRenderer.Orientation;

import android.graphics.Canvas;
import android.graphics.Paint;

/**
 * The combined XY chart rendering class.
 */
public class CombinedXYChart extends XYChart {
  /** The embedded XY charts. */
  private XYChart[] mCharts;
  /** The supported charts for being combined. */
  private Class[] xyChartTypes = new Class[] { TimeChart.class, LineChart.class,
      CubicLineChart.class, BarChart.class, BubbleChart.class, ScatterChart.class,
      RangeBarChart.class};

    /** The number of milliseconds in a day. */
	public static final long DAY = 24 * 60 * 60 * 1000;
	/** The date format pattern to be used in formatting the X axis labels. */
	private String mDateFormat;
	/** If X axis value selection algorithm to be used. */
	private boolean mXAxisSmart = true;
	/** The starting point for labels. */
	private Double mStartPoint;
	/** Flat to store if mDateFormat is set. */
	private boolean mFormatSet=false;
  
  
  /**
   * Builds a new combined XY chart instance.
   * 
   * @param dataset the multiple series dataset
   * @param renderer the multiple series renderer
   * @param types the XY chart types
   */
  public CombinedXYChart(XYMultipleSeriesDataset dataset, XYMultipleSeriesRenderer renderer,
      String[] types) {
    super(dataset, renderer);
    int length = types.length;
    mCharts = new XYChart[length];
    for (int i = 0; i < length; i++) {
      try {
        mCharts[i] = getXYChart(types[i]);
      } catch (Exception e) {
        // ignore
      }
      if (mCharts[i] == null) {
        throw new IllegalArgumentException("Unknown chart type " + types[i]);
      } else {
        XYMultipleSeriesDataset newDataset = new XYMultipleSeriesDataset();
        newDataset.addSeries(dataset.getSeriesAt(i));
        XYMultipleSeriesRenderer newRenderer = new XYMultipleSeriesRenderer(renderer.getScalesCount());
        // TODO: copy other parameters here
        newRenderer.setBarSpacing(renderer.getBarSpacing());
        newRenderer.setPointSize(renderer.getPointSize());
        int scale = dataset.getSeriesAt(i).getScaleNumber();
        if (renderer.isMinXSet(scale)) {
          newRenderer.setXAxisMin(renderer.getXAxisMin(scale),scale);
        }
        if (renderer.isMaxXSet(scale)) {
          newRenderer.setXAxisMax(renderer.getXAxisMax(scale),scale);
        }
        if (renderer.isMinYSet(scale)) {
          newRenderer.setYAxisMin(renderer.getYAxisMin(scale),scale);
        }
        if (renderer.isMaxYSet(scale)) {
          newRenderer.setYAxisMax(renderer.getYAxisMax(scale),scale);
        }
        newRenderer.addSeriesRenderer(renderer.getSeriesRendererAt(i));
        mCharts[i].setDatasetRenderer(newDataset, newRenderer);
      }
    }
  }

  /**
   * Returns a chart instance based on the provided type.
   * 
   * @param type the chart type
   * @return an instance of a chart implementation
   * @throws IllegalAccessException
   * @throws InstantiationException
   */
  private XYChart getXYChart(String type) throws IllegalAccessException, InstantiationException {
    XYChart chart = null;
    int length = xyChartTypes.length;
    
    for (int i = 0; i < length && chart == null; i++) {
      XYChart newChart = (XYChart) xyChartTypes[i].newInstance();
      if (type.equals(newChart.getChartType())) {
        chart = newChart;
      }
    }
    return chart;
  }

  /**
   * The graphical representation of a series.
   * 
   * @param canvas the canvas to paint to
   * @param paint the paint to be used for drawing
   * @param points the array of points to be used for drawing the series
   * @param seriesRenderer the series renderer
   * @param yAxisValue the minimum value of the y axis
   * @param seriesIndex the index of the series currently being drawn
   * @param startIndex the start index of the rendering points
   */
  public void drawSeries(Canvas canvas, Paint paint, float[] points,
      SimpleSeriesRenderer seriesRenderer, float yAxisValue, int seriesIndex, int startIndex) {
    mCharts[seriesIndex].setScreenR(getScreenR());
    mCharts[seriesIndex].setCalcRange(getCalcRange(mDataset.getSeriesAt(seriesIndex)
        .getScaleNumber()), 0);
    mCharts[seriesIndex].drawSeries(canvas, paint, points, seriesRenderer, yAxisValue, 0,
        startIndex);
  }

  @Override
  protected ClickableArea[] clickableAreasForPoints(float[] points, double[] values,
      float yAxisValue, int seriesIndex, int startIndex) {
    return mCharts[seriesIndex].clickableAreasForPoints(points, values, yAxisValue, 0, startIndex);
  }

  @Override
  protected void drawSeries(XYSeries series, Canvas canvas, Paint paint, List<Float> pointsList,
      SimpleSeriesRenderer seriesRenderer, float yAxisValue, int seriesIndex, Orientation or,
      int startIndex) {
    mCharts[seriesIndex].setScreenR(getScreenR());
    mCharts[seriesIndex].setCalcRange(getCalcRange(mDataset.getSeriesAt(seriesIndex)
        .getScaleNumber()), 0);
    mCharts[seriesIndex].drawSeries(series, canvas, paint, pointsList, seriesRenderer, yAxisValue,
        0, or, startIndex);
  }

  /**
   * Returns the legend shape width.
   * 
   * @param seriesIndex the series index
   * @return the legend shape width
   */
  public int getLegendShapeWidth(int seriesIndex) {
    return mCharts[seriesIndex].getLegendShapeWidth(0);
  }

  /**
   * The graphical representation of the legend shape.
   * 
   * @param canvas the canvas to paint to
   * @param renderer the series renderer
   * @param x the x value of the point the shape should be drawn at
   * @param y the y value of the point the shape should be drawn at
   * @param seriesIndex the series index
   * @param paint the paint to be used for drawing
   */
  public void drawLegendShape(Canvas canvas, SimpleSeriesRenderer renderer, float x, float y,
      int seriesIndex, Paint paint) {
    mCharts[seriesIndex].drawLegendShape(canvas, renderer, x, y, 0, paint);
  }

  /**
   * Returns the chart type identifier.
   * 
   * @return the chart type
   */
  public String getChartType() {
    return "Combined";
  }

  
	/**
	 * Returns the date format pattern to be used for formatting the X axis
	 * labels.
	 * 
	 * @return the date format pattern for the X axis labels
	 */
	public String getDateFormat() {
		return mDateFormat;
	}

	/**
	 * Sets the date format pattern to be used for formatting the X axis labels.
	 * 
	 * @param format
	 *            the date format pattern for the X axis labels. If null, an
	 *            appropriate default format will be used.
	 */
	public void setDateFormat(String format) {
		mDateFormat = format;
		mFormatSet=true;
	}

	/**
	 * If X axis smart values to be used.
	 * 
	 * @return if smart values to be used
	 */
	public boolean isXAxisSmart() {
		return mXAxisSmart;
	}

	/**
	 * Sets if X axis smart values to be used.
	 * 
	 * @param smart
	 *            smart values to be used
	 */
	public void setXAxisSmart(boolean smart) {
		mXAxisSmart = smart;
	}

	public boolean isFormatSet() {
		return mFormatSet;
	}

	//public void setFormatSet(boolean mFormatSet) {
	//	this.mFormatSet = mFormatSet;
	//}

	/**
	 * The graphical representation of the labels on the X axis.
	 * 
	 * @param xLabels
	 *            the X labels values
	 * @param xTextLabelLocations
	 *            the X text label locations
	 * @param canvas
	 *            the canvas to paint to
	 * @param paint
	 *            the paint to be used for drawing
	 * @param left
	 *            the left value of the labels area
	 * @param top
	 *            the top value of the labels area
	 * @param bottom
	 *            the bottom value of the labels area
	 * @param xPixelsPerUnit
	 *            the amount of pixels per one unit in the chart labels
	 * @param minX
	 *            the minimum value on the X axis in the chart
	 * @param maxX
	 *            the maximum value on the X axis in the chart
	 */
	@Override
	protected void drawXLabels(List<Double> xLabels, Double[] xTextLabelLocations, Canvas canvas, Paint paint, int left, int top, int bottom,
			double xPixelsPerUnit, double minX, double maxX) {
		if(!isFormatSet()){
			super.drawXLabels(xLabels, xTextLabelLocations, canvas, paint, left,
				top, bottom, xPixelsPerUnit, minX, maxX);
		}
		else{
			int length = xLabels.size();
			if (length > 0) {
				boolean showXLabels = mRenderer.isShowXLabels();
				boolean showLabels = mRenderer.isShowLabels();
				boolean showGridY = mRenderer.isShowGridY();
				DateFormat format = getDateFormat(xLabels.get(0),
						xLabels.get(length - 1));
				for (int i = 0; i < length; i++) {
					long label = Math.round(xLabels.get(i));
					float xLabel = (float) (left + xPixelsPerUnit
							* (label - minX));
					if (showLabels || showXLabels) {
						paint.setColor(mRenderer.getXLabelsColor());
						canvas.drawLine(xLabel, bottom, xLabel, bottom
								+ mRenderer.getLabelsTextSize() / 3, paint);
						drawText(canvas, format.format(new Date(label)),
								xLabel, bottom + mRenderer.getLabelsTextSize()
										* 4 / 3, paint,
								mRenderer.getXLabelsAngle());
					}
					if (showGridY) {
						paint.setColor(mRenderer.getGridColor());
						canvas.drawLine(xLabel, bottom, xLabel, top, paint);
					}
				}
			}
			drawXTextLabels(xTextLabelLocations, canvas, paint, true, left,
					top, bottom, xPixelsPerUnit, minX, maxX);
		}
	}

	/**
	 * Returns the date format pattern to be used, based on the date range.
	 * 
	 * @param start
	 *            the start date in milliseconds
	 * @param end
	 *            the end date in milliseconds
	 * @return the date format
	 */
	private DateFormat getDateFormat(double start, double end) {
		if (mDateFormat != null) {
			SimpleDateFormat format = null;
			try {
				format = new SimpleDateFormat(mDateFormat);
				return format;
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		DateFormat format = SimpleDateFormat.getDateInstance(SimpleDateFormat.MEDIUM);
		double diff = end - start;
		if (diff > DAY && diff < 5 * DAY) {
			format = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.SHORT, SimpleDateFormat.SHORT);
		} else if (diff < DAY) {
			format = SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM);
		}
		return format;
	}

	protected List<Double> getXLabels(double min, double max, int count) {
		
		if(!isFormatSet()){
			return super.getXLabels(min,max,count);
		}
		else{
			if (!mXAxisSmart) {
				return super.getXLabels(min, max, count);
			}
			if (mStartPoint == null) {
				mStartPoint = min - (min % DAY) + DAY
						+ new Date(Math.round(min)).getTimezoneOffset() * 60
						* 1000;
			}
			if (count > 25) {
				count = 25;
			}
			final double cycleMath = (max - min) / count;
			double cycle = DAY;

			if (cycleMath <= DAY) {
				while (cycleMath < cycle / 2) {
					cycle = cycle / 2;
				}
			} else {
				while (cycleMath > cycle) {
					cycle = cycle * 2;
				}
			}

			final List<Double> result = new ArrayList<Double>();
			double val = mStartPoint - Math.floor((mStartPoint - min) / cycle)
					* cycle;
			int i = 0;
			while (val < max && i++ <= count) {
				result.add(val);
				val += cycle;
			}

			return result;
		}
	}

	protected String getFormatedXLabel(long key) {
		if (mDateFormat != null) {
			SimpleDateFormat format = null;
			try {
				format = new SimpleDateFormat(mDateFormat);
				return (format.format(new Date(key)));
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return String.valueOf(key);
	}

  
}
